#include "AlgebraModule.h"

#include "Factory.h"
#include "TimeSteppers.h"

#ifdef SG_USE_MPI
#   include "sgPetsc.h"

#   ifdef SG_USE_MKL
#       include "sgCPardiso.h"
#   endif

#else
#   include "sgEigen.h"
#   ifdef SG_USE_SUPERLU
#       include "sgSuperLU.h"
#   endif

#   ifdef SG_USE_MKL
#       include "sgPardiso.h"
#   endif
#endif

#include <iostream>

using namespace SG::Base;
using namespace SG::Algebra;

namespace
{
    struct AlgebraModule
    {
        AlgebraModule ();
    } module;

#ifdef SG_USE_MPI
    std::shared_ptr<MpiEnvironment> env;
#endif

}  // namespace

//=============================================================================
// Module

AlgebraModule::AlgebraModule ()
{
    // the factory will take ownership of the producers.
    // just feel free to create the producers on the heap.

    // linear solvers
#ifdef SG_USE_MPI
    new GeneralProducer<TPetscKsp<Real_t>> ("TPetscKsp<Real_t>");

#   ifdef SG_USE_MKL
    new GeneralProducer<CPardiso<Real_t>> ("CPardiso<Real_t>");
    new GeneralProducer<CPardiso<Complex_t>> ("CPardiso<Complex_t>");
#   endif
#else
    new GeneralProducer<TEigenLLT<Real_t>> ("TEigenLLT<Real_t>");

#   ifdef SG_USE_SUPERLU
    new GeneralProducer<sgSuperLU> ("sgSuperLU");
#   endif

#   ifdef SG_USE_MKL
    // new GeneralProducer<sgPardiso> ("sgPardiso");
    // new GeneralProducer<sgPardiso> ("sgPardiso");  // for compatibilitie
    new GeneralProducer<TPardiso<Real_t>> ("TPardiso<Real_t>");
    new GeneralProducer<TPardiso<Complex_t>> ("TPardiso<Complex_t>");
#   endif
#endif

    // time steppers
    new GeneralProducer<CentralDifference> ("CentralDifference");
    new GeneralProducer<ModifiedCentralDifference> ("ModifiedCentralDifference");
    new GeneralProducer<NewmarkBeta> ("NewmarkBeta");
    new GeneralProducer<WilsonTheta> ("WilsonTheta");
    new GeneralProducer<GeneralizedAlpha> ("GeneralizedAlpha");
    new GeneralProducer<Bathe> ("Bathe");
}

SG_NAMESPACE_OPEN
namespace Algebra
{
    void Initialize (int argc, char** argv)
    {
#ifdef SG_VERBOSE
        std::cout << "\n Algebra Configurations Summary" << std::endl;
#endif

#ifdef SG_USE_MPI

        env.reset (new PetscEnvironment (argc, argv));

#   ifdef SG_VERBOSE
        std::cout << "\tMPI Environment:" << std::endl
                  << "\t\tVersion: " << env->version () << std::endl
                  << "\t\trank: " << env->world_rank () << std::endl
                  << "\t\tsize: " << env->world_size () << std::endl
                  << "\t\tprocessor name: " << env->processor_name () << std::endl;
#   endif
#endif

#ifdef SG_VERBOSE
        std::cout << "\tProducers supported: " << std::endl;
        auto producers = Factory::GetInstance ()->CanProduce ();
        for (auto it = producers.begin (); it != producers.end (); ++it)
        {
            std::cout << "\t\t" << *it << std::endl;
        }
        std::cout << std::endl << std::endl;
#endif
    }

    void Finalize ()
    {
#ifdef SG_USE_MPI
        env.reset (( MpiEnvironment* ) nullptr);
#endif
    }
}

SG_NAMESPACE_CLOSE
